package edu.corn.job.core.context;


import cn.hutool.core.util.ObjectUtil;
import edu.corn.job.core.engine.JobEngine;
import edu.corn.job.core.engine.persistent.JobPersistent;
import edu.corn.job.core.processor.AbstractJobProcessor;
import edu.corn.job.core.processor.CallFallback;
import edu.corn.job.core.processor.adapter.JobAdapter;
import edu.corn.job.core.processor.adapter.factory.JobAdapterFactory;
import edu.corn.job.core.statemachine.JobStateEnum;
import edu.corn.statemachine.StateMachineSupport;
import lombok.Getter;
import lombok.Setter;

import java.io.Serial;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Set;
import java.util.Stack;

/**
 * 代表一次任务提交的全部上下文。同时是带有时间状态上下文的。
 * <p>
 * 包含一些主要的信息。
 * - 上下文信息map
 * - processor根执行器
 * - 当前执行进度。（考虑如何保存job执行中途的执行状态。）
 * - 执行结果
 * - 执行标识
 * - 应该还要保存class文件。
 */
public class JobRunningContext implements StateMachineSupport<JobStateEnum>, Serializable {

    @Serial
    private static final long serialVersionUID = 1L;

    /**
     * 当前正在执行的processor。
     */
    @Getter
    @Setter
    private transient AbstractJobProcessor processor;

    /**
     * 代表当前执行job的id。
     */
    @Getter
    @Setter
    private String jobId;

    /**
     * 日志查询的traceId。
     */
    @Getter
    @Setter
    private String traceId;

    /**
     * 执行的根job类信息。
     */
    @Getter
    @Setter
    private Class<? extends AbstractJobProcessor> processorClass;

    /**
     * 持有jobAdapter，便于从挂起到运行时可以直接运行。
     */
    @Getter
    @Setter
    private transient JobAdapter jobAdapter;

    /**
     * job引擎
     */
    @Getter
    @Setter
    private transient JobEngine jobEngine;


    /**
     * 当前执行的job的状态。
     */
    private JobStateEnum state;

    /**
     * 可能存在的回调。
     */
    @Getter
    @Setter
    private CallFallback callable;

    /**
     * 执行中可能存在的异常。
     */
    @Getter
    @Setter
    private transient Throwable throwable = null;


    /**
     * 执行过程中的参数上下文。
     */
    private final ContextCommandManager context = new ContextCommandManager();

    /**
     * 记录调用栈。
     */
    private final transient Stack<AbstractJobProcessor> processorStack = new Stack<>();

    private final transient HashMap<String, Integer> lockMap = new HashMap<>();

    private void addLock(HashMap<String, Integer> lockMap, Set<? extends Enum<?>> locks) {
        for (Enum<?> lock : locks) {
            lockMap.compute(String.valueOf(lock), (k, v) -> (v == null) ? 1 : v + 1);
        }
    }

    public void addLock(Set<? extends Enum<?>> locks) {
        addLock(this.lockMap, locks);
    }

    private void unLock(HashMap<String, Integer> lockMap, Set<? extends Enum<?>> locks) {
        for (Enum<?> lock : locks) {
            lockMap.compute(String.valueOf(lock), (k, v) -> ObjectUtil.equals(v, 1) ? lockMap.remove(String.valueOf(lock)) : v - 1);
        }
    }

    public void unLock(Set<? extends Enum<?>> locks) {
        addLock(this.lockMap, locks);
    }

    public void push(AbstractJobProcessor processor) {
        processorStack.push(processor);
    }

    public AbstractJobProcessor pop() {
        processor = processorStack.pop();
        return processor;
    }


    public Object getParam(String key) {
        return context.getParam(key);
    }


    /**
     * 返回一个版本号，代表context的版本。在必要时可能会根据版本号进行回滚，所以需要保存版本号。
     * @param key
     * @param value
     * @return
     */
    public Integer putParam(String key, Serializable value) {
        return context.putParam(key, value);
    }

    // 除了context需要被回滚，job进度应该也要被回滚。
    public void rollback(Integer version){
        context.rollbackToVersion(version);

    }

    public void putMasterId(String jobId) {
        this.jobId = jobId;
    }

    public String getMasterId() {
        return jobId;
    }

    @Override
    public JobStateEnum getState() {
        return state;
    }

    @Override
    public void setState(JobStateEnum state) {
        this.state = state;
    }


    /**
     * 对外提供的执行的接口。
     */
    public void run() {
        if (this.jobAdapter == null) {
            this.jobAdapter = JobAdapterFactory.getAdapter(processor, jobEngine);

        }
        this.jobAdapter.executeJob(this);
    }

    /**
     * 将当前执行的job进行进度持久化。
     * 要对进度进行持久化吗？如果对进度进行持久化，要保存哪些上下文？
     * - context
     * - 调用栈
     * - 似乎只需要上述两个东西。
     *
     * @param jobPersistent
     * @throws Exception
     */
    public void save(JobPersistent jobPersistent) throws Exception {

    }

    public String currentJob() {
        // 调用时应该暂停执行。当前job的执行。


        HashMap<String, Integer> tempMap = new HashMap<>(lockMap);
        Stack<AbstractJobProcessor> tempStack = new Stack<>();
        String currentJobName = null;
        // 从栈中弹出，当map为空时是最后一个申请锁的job。
        while (true) {
            AbstractJobProcessor job = tempStack.pop();
            Set<? extends Enum<?>> mutexes = job.mutexes();
            unLock(tempMap, mutexes);
            if (tempMap.isEmpty()) {
                currentJobName = job.getName();
                break;
            }
        }

        return currentJobName;
    }

    public static JobRunningContext reload(JobPersistent jobPersistent) throws Exception {
        JobRunningContext context = new JobRunningContext();
        return context;
    }

}
